package com.basin.common.glc.func;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.basin.common.glc.constance.BasinLogConst;
import com.basin.common.glc.context.BasinLogContext;
import com.basin.common.glc.event.BasinLogEvent;
import com.basin.common.glc.pojo.BasinLogVO;
import com.basin.common.glc.wrapper.CachedBodyHttpServletRequest;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: Sam ZHONG | sammy1997@aliyun.com
 * @date: 2025/6/21 18:09
 * @description:
 */
@Aspect
@Component
@Slf4j
public class ControllerAspect {
    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void controllerPointcut() {
    }

    @PostConstruct
    public void init() {
        log.info("ControllerAspect init start");
    }

    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        BasinLogVO logVO = new BasinLogVO();
        BasinLogContext.set(logVO);
        long startTime = System.currentTimeMillis();
        logVO.setStartTime(startTime);
        String traceId = UUID.fastUUID().toString(true);
        MDC.put(BasinLogConst.REQUEST_ID, traceId);
        logVO.setTraceId(traceId);
        logVO.setLogId(UUID.randomUUID().toString(true));
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(request);
        String requestURI = cachedBodyHttpServletRequest.getRequestURI();
        logVO.setRequestUri(requestURI);
        Map<String, String[]> parameterMap = cachedBodyHttpServletRequest.getParameterMap();
        if (CollUtil.isNotEmpty(parameterMap)) {
            Map<String, String> tmpMap = new HashMap<>(parameterMap.size());
            parameterMap.forEach((key, value) -> {
                tmpMap.put(key, value[0]);
            });
            logVO.setParams(tmpMap);
        }
        Enumeration<String> headerNames = cachedBodyHttpServletRequest.getHeaderNames();
        if (CollUtil.isNotEmpty(headerNames)) {
            Map<String, String> tmpMap = new HashMap<>(16);
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                String headerValue = cachedBodyHttpServletRequest.getHeader(headerName);
                tmpMap.put(headerName, headerValue);
            }
            logVO.setHeaders(tmpMap);
        }
        Object proceed = joinPoint.proceed();
        long endTime = System.currentTimeMillis();
        logVO.setResult(proceed);
        logVO.setEndTime(endTime);
        logVO.setCostTime(endTime - startTime);
        MDC.remove(BasinLogConst.REQUEST_ID);
        log.info(JSON.toJSONString(logVO, JSONWriter.Feature.PrettyFormat));
        SpringUtil.getApplicationContext().publishEvent(new BasinLogEvent(JSON.toJSONString(logVO)));
        BasinLogContext.clear();
        return proceed;
    }
}
